﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#ifndef __SERVER_INFO_INL__
#define __SERVER_INFO_INL__

#pragma once

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_INFO : 服务信息结构方法定义
INLINE tagSERVER_INFO::tagSERVER_INFO(void)
{
    Reset();
}

INLINE tagSERVER_INFO::~tagSERVER_INFO(void)
{
    usUpdate = (UShort)SERVERU_NONE;
}

INLINE size_t tagSERVER_INFO::Length(UShort us)
{
    size_t stSize = sizeof(tagSERVER_STATUS);

    stSize += sizeof(UInt); // uSync

    if ((us & (UShort)SERVERU_STRADDR) != 0)
    {
        stSize += StrAddr.Length();
    }
    if ((us & (UShort)SERVERU_ROUTINE) != 0)
    {
        stSize += strRoutine.Length(true);
        stSize += strInstance.Length(true);
    }
    return stSize;
}

INLINE void tagSERVER_INFO::Status(CStream& Stream, UShort us)
{
    if (Stream.IsRead())
    {
        Stream >> uTotal >> uOnline >> usLoad >> usRate >> usId >> usUpdate;
    }
    else
    {
        Stream << uTotal << uOnline << usLoad << usRate << usId;
        ((us & (UShort)SERVERU_OKAY) == 0) ? (Stream << usUpdate) : (Stream << (UShort)SERVERU_LINK);
    }
}

INLINE void tagSERVER_INFO::Info(CStream& Stream, UShort us)
{
    if ((us & (UShort)SERVERU_SYNCMASK) == 0)
    {
        Stream.IsRead() ? (Stream >> uSync) : (Stream << uSync);
    }
    else
    {
        UInt uSyncNull = 0;
        Stream.IsRead() ? (Stream >> uSyncNull) : (Stream << uSyncNull);
    }
    if ((us & (UShort)SERVERU_STRADDR) != 0)
    {
        StrAddr.Serialize(Stream);
    }
    if ((us & (UShort)SERVERU_ROUTINE) != 0)
    {
        Stream.IsRead() ? (Stream >> strRoutine >> strInstance) : (Stream << strRoutine << strInstance);
    }
}

INLINE void tagSERVER_INFO::Copy(const tagSERVER_STATUS& ss)
{
    uTotal    = ss.uTotal;
    uOnline   = ss.uOnline;
    usLoad    = ss.usLoad;
    usRate    = ss.usRate;
    usId      = ss.usId;
    usUpdate  = ss.usUpdate;
    if (usUpdate != (UShort)SERVERU_LINK)
    {
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::IncrTotal(bool bLoad, UInt uIncr)
{
    CAtomics::Add<UInt>(&uTotal, uIncr);
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) + usRate < (UShort)COMMONC_OVER_LOAD))
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad + usRate));
    }
    SetUpdate();
}

INLINE void tagSERVER_INFO::DecrTotal(bool bLoad, UInt uDecr)
{
    if (CAtomics::CompareExchange<UInt>(&uTotal, 0, 0) > uDecr)
    {
        CAtomics::Exchange<UInt>(&uTotal, (uTotal - uDecr));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_OVER_LOAD))
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) > usRate)
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad - usRate));
        }
        else
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        }
    }
    SetUpdate();
}

INLINE void tagSERVER_INFO::IncrOnline(bool bLoad, UInt uIncr)
{
    CAtomics::Add<UInt>(&uOnline, uIncr);
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) + usRate < (UShort)COMMONC_OVER_LOAD))
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad + usRate));
    }
    SetUpdate();
}

INLINE void tagSERVER_INFO::DecrOnline(bool bLoad, UInt uDecr)
{
    if (CAtomics::CompareExchange<UInt>(&uOnline, 0, 0) > uDecr)
    {
        CAtomics::Exchange<UInt>(&uOnline, (uOnline - uDecr));
    }
    else
    {
        CAtomics::Exchange<UInt>(&uOnline, 0);
    }
    if (bLoad && ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_OVER_LOAD))
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) > usRate)
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(usLoad - usRate));
        }
        else
        {
            CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        }
    }
    SetUpdate();
}

INLINE void tagSERVER_INFO::Zero(void)
{
    uTotal   = 0;
    uOnline  = 0;
    usLoad   = 0;
    usUpdate = (UShort)SERVERU_NONE;
}

INLINE void tagSERVER_INFO::Reset(void)
{
    MM_SAFE::Set(this, 0, sizeof(tagSERVER_STATUS));
    uSync = 0;
    StrAddr.Reset();
    strRoutine.Empty();
    strInstance.Empty();
}

INLINE void tagSERVER_INFO::SetUpdate(UShort us)
{
    usUpdate = us;
}

INLINE void tagSERVER_INFO::ClearOnline(bool bTotal)
{
    CAtomics::Exchange<UInt>(&uOnline, 0);
    if (bTotal)
    {
        CAtomics::Exchange<UInt>(&uTotal, 0);
    }
}

INLINE void tagSERVER_INFO::Clear(bool bTotal)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) <= (UShort)COMMONC_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, bTotal ? (Short)(UShort)(uTotal * usRate) : (Short)(UShort)(uOnline * usRate));
        SetUpdate();
    }
    else if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_MAY_DAY)
    {
        CAtomics::Decrement16<Short>((PShort)&usLoad);
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::ClearLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) <= (UShort)COMMONC_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, 0);
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::SetBusyLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_BUSY_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)COMMONC_BUSY_LOAD);
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::SetFullLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_FULL_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)COMMONC_FULL_LOAD);
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::SetOverLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_OVER_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)COMMONC_OVER_LOAD);
        SetUpdate();
    }
}

INLINE void tagSERVER_INFO::SetWarnLoad(void)
{
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_WARN_LOAD)
    {
        CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)COMMONC_WARN_LOAD);
    }
    else
    {
        CAtomics::Increment16<Short>((PShort)&usLoad);
    }
    SetUpdate();
}

INLINE void tagSERVER_INFO::SetMayDay(void)
{
    CAtomics::Exchange16<Short>((PShort)&usLoad, (Short)(UShort)COMMONC_MAY_DAY);
    SetUpdate();
}

INLINE bool tagSERVER_INFO::IsBusyLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_FULL_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_BUSY_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_BUSY_LOAD);
}

INLINE bool tagSERVER_INFO::IsFullLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_OVER_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_FULL_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_FULL_LOAD);
}

INLINE bool tagSERVER_INFO::IsOverLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_WARN_LOAD)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_OVER_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_OVER_LOAD);
}

INLINE bool tagSERVER_INFO::IsWarnLoad(bool bExact)
{
    if (bExact)
    {
        if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) < (UShort)COMMONC_MAY_DAY)
        {
            return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_WARN_LOAD);
        }
        return false;
    }
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_WARN_LOAD);
}

INLINE bool tagSERVER_INFO::IsMayDay(void)
{
    return ((UShort)CAtomics::CompareExchange16<Short>((PShort)&usLoad, 0, 0) >= (UShort)COMMONC_MAY_DAY);
}

INLINE bool tagSERVER_INFO::IsUpdated(void)
{
    return (usUpdate != (UShort)SERVERU_NONE);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_DATA : 服务信息数据
INLINE tagSERVER_DATA::tagSERVER_DATA(void)
{
    ResetData();
}

INLINE tagSERVER_DATA::~tagSERVER_DATA(void)
{
    usUpdate = (UShort)SERVERU_NONE;
}

INLINE void tagSERVER_DATA::ResetData(void)
{
    nStatus = SERVERS_NONE;
    uRank   = 0;
    uQCheck = 0;
    sListen = 0;
    MM_SAFE::Set(sConnect, 0, sizeof(Socket_t) * COMMONR_MAX);
    MM_SAFE::Set(nLink, 0, sizeof(Int) * COMMONR_MAX);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// tagSERVER_CLUSTER : 服务信息集群结构定义
INLINE tagSERVER_CLUSTER::tagSERVER_CLUSTER(void)
{
    Reset();
}

INLINE tagSERVER_CLUSTER::~tagSERVER_CLUSTER(void)
{
    Status.usUpdate = (UShort)SERVERU_NONE;
}

INLINE void tagSERVER_CLUSTER::Link(const tagSERVER_STATUS& ss)
{
    CAtomics::Add<UInt>(&Status.uTotal,  ss.uTotal);
    CAtomics::Add<UInt>(&Status.uOnline, ss.uOnline);
    CAtomics::Increment16<Short>((PShort)&Status.usLoad);
    SetUpdate();
}

INLINE void tagSERVER_CLUSTER::Update(const tagSERVER_STATUS& ssIncr, const tagSERVER_STATUS& ssDecr)
{
    CAtomics::Add<UInt>(&Status.uTotal, ssIncr.uTotal);
    if (CAtomics::CompareExchange<UInt>(&Status.uTotal, 0, 0) > ssDecr.uTotal)
    {
        CAtomics::Exchange<UInt>(&Status.uTotal, (Status.uTotal - ssDecr.uTotal));
    }
    else
    {
        CAtomics::Exchange<UInt>(&Status.uTotal, 0);
    }
    CAtomics::Add<UInt>(&Status.uOnline, ssIncr.uOnline);
    if (CAtomics::CompareExchange<UInt>(&Status.uOnline, 0, 0) > ssDecr.uOnline)
    {
        CAtomics::Exchange<UInt>(&Status.uOnline, (Status.uOnline - ssDecr.uOnline));
    }
    else
    {
        CAtomics::Exchange<UInt>(&Status.uOnline, 0);
    }
    SetUpdate();
}

INLINE void tagSERVER_CLUSTER::Unlink(const tagSERVER_STATUS& ss)
{
    if (CAtomics::CompareExchange<UInt>(&Status.uTotal, 0, 0) > ss.uTotal)
    {
        CAtomics::Exchange<UInt>(&Status.uTotal, (Status.uTotal - ss.uTotal));
    }
    else
    {
        CAtomics::Exchange<UInt>(&Status.uTotal, 0);
    }
    if (CAtomics::CompareExchange<UInt>(&Status.uOnline, 0, 0) > ss.uOnline)
    {
        CAtomics::Exchange<UInt>(&Status.uOnline, (Status.uOnline - ss.uOnline));
    }
    else
    {
        CAtomics::Exchange<UInt>(&Status.uOnline, 0);
    }
    if ((UShort)CAtomics::CompareExchange16<Short>((PShort)&Status.usLoad, 0, 0) > 0)
    {
        CAtomics::Decrement16<Short>((PShort)&Status.usLoad);
    }
    SetUpdate();
}

INLINE void tagSERVER_CLUSTER::SetUpdate(UShort us)
{
    Status.usUpdate = us;
}

INLINE void tagSERVER_CLUSTER::Reset()
{
    MM_SAFE::Set(&Status, 0, sizeof(tagSERVER_STATUS));
}

INLINE void tagSERVER_CLUSTER::Update(void)
{
    DEV_DEBUG(TF("Update Size = %d"), Info.GetSize());
    for (PINDEX index = Info.GetFirstIndex(); index != nullptr;)
    {
        PAIR_INFO* pPair = Info.GetNext(index);
        if (pPair->m_V.usUpdate & (UShort)(SERVERU_SYNC|SERVERU_LINK))
        {
            DEV_DEBUG(TF("Update %X status to okay %p"), pPair->m_V.usUpdate, pPair->m_K);
            pPair->m_V.SetUpdate((UShort)SERVERU_OKAY);
        }
        else if (pPair->m_V.usUpdate & (UShort)SERVERU_UNLINK)
        {
            DEV_DEBUG(TF("Update %X status to remove %p"), pPair->m_V.usUpdate, pPair->m_K);
            Info.RemoveAt(reinterpret_cast<PINDEX>(pPair));
        }
    }
}

INLINE bool tagSERVER_CLUSTER::Read(CStream& Stream, UShort us)
{
    bool bRet = false;
    if (Stream.IsRead())
    {
        ULLong ullKey = 0;
        Stream >> ullKey;

        SERVER_INFO si;
        si.Status(Stream, us);

        PINDEX index = Info.FindIndex(ullKey);
        switch (si.usUpdate)
        {
        case SERVERU_LINK:
            {
                si.Info(Stream, us);
                if (index == nullptr)
                {
                    if ((us & (UShort)SERVERU_UPDATE) != 0)
                    {
                        si.SetUpdate((UShort)SERVERU_OKAY);
                    }
                    Info.Add(ullKey, si);
                    bRet = true;
                    DEV_DEBUG(TF("Read %X status link %p"), us, ullKey);
                }
                else
                {
                    DEV_DEBUG(TF("Read %X status link %p failed"), us, ullKey);
                }
            }
            break;
        case SERVERU_SYNC:
            {
                if (index != nullptr)
                {
                    if ((us & (UShort)SERVERU_UPDATE) != 0)
                    {
                        si.SetUpdate((UShort)SERVERU_OKAY);
                    }
                    Info[index].Copy(si);
                    bRet = true;
                    DEV_DEBUG(TF("Read %X status sync %p"), us, ullKey);
                }
                else
                {
                    DEV_DEBUG(TF("Read %X status sync %p failed"), us, ullKey);
                }
            }
            break;
        case SERVERU_UNLINK:
            {
                if (index != nullptr)
                {
                    if ((us & (UShort)SERVERU_UPDATE) != 0)
                    {
                        Info.RemoveAt(index);
                    }
                    else
                    {
                        Info[index].SetUpdate((UShort)SERVERU_UNLINK);
                    }
                    bRet = true;
                    DEV_DEBUG(TF("Read %X status unlink %p"), us, ullKey);
                }
                else
                {
                    DEV_DEBUG(TF("Read %X status unlink %p failed"), us, ullKey);
                }
            }
            break;
        default:
            {
                DEV_WARN(TF("Read %X status unknown %p"), us, ullKey);
            }
        }
    }
    return bRet;
}

INLINE bool tagSERVER_CLUSTER::Write(PAIR_INFO* pPair, CStream& Stream, UShort us)
{
    if ((pPair != nullptr) && (pPair->m_V.usUpdate & us))
    {
        Stream << pPair->m_K;
        if ((us & (UShort)SERVERU_OKAY) != 0)
        {
            pPair->m_V.Status(Stream, us);
            pPair->m_V.Info(Stream, us);
            DEV_DEBUG(TF("Write %X status sync & okay %p"), us, pPair->m_K);
        }
        else
        {
            switch (pPair->m_V.usUpdate)
            {
            case SERVERU_LINK:
                {
                    pPair->m_V.Status(Stream, us);
                    pPair->m_V.Info(Stream, us);
                    DEV_DEBUG(TF("Write %X status link %p"), us, pPair->m_K);
                }
                break;
            case SERVERU_SYNC:
            case SERVERU_UNLINK:
                {
                    pPair->m_V.Status(Stream, us);
                    DEV_DEBUG(TF("Write %X status sync & unlink %p"), us, pPair->m_K);
                }
                break;
            default:
                {
                    DEV_WARN(TF("Write %X status unknown %p"), us, pPair->m_K);
                }
            }
        }
        return true;
    }
    return false;
}

INLINE bool tagSERVER_CLUSTER::CheckWrite(UInt& uCount, PINDEX& index, CStream& Stream, UShort us)
{
    if (index != nullptr)
    {
        PAIR_INFO* pPair = Info.GetAt(index);
        if (Stream.Rest() >= (sizeof(pPair->m_K) + pPair->m_V.Length(us)))
        {
            uCount += Write(pPair, Stream, us) ? 1 : 0;
            Info.GetNext(index);
            return true;
        }
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 服务信息同步结构定义
INLINE tagSERVER_SYNC::tagSERVER_SYNC(void)
{
}

INLINE tagSERVER_SYNC::~tagSERVER_SYNC(void)
{
}

INLINE bool tagSERVER_SYNC::AddSync(Socket_t sSocket)
{
    CSyncLockWaitScope scope(Lock);
    return Sock.Add(sSocket);
}

INLINE bool tagSERVER_SYNC::RemoveSync(Socket_t sSocket)
{
    CSyncLockWaitScope scope(Lock);
    return Sock.Remove(sSocket);
}

INLINE bool tagSERVER_SYNC::GetSync(ARY_SYNC& aSync)
{
    CSyncLockWaitScope scope(Lock);
    if (Sock.GetSize() > 0)
    {
        aSync = Sock;
        return true;
    }
    return false;
}

INLINE void tagSERVER_SYNC::Reset(void)
{
    Sock.RemoveAll();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 服务信息交换链定义
template <typename TCHAIN>
INLINE tagSERVER_CHAIN<TCHAIN>::tagSERVER_CHAIN(void)
: uIndex(0)
{
}

template <typename TCHAIN>
INLINE tagSERVER_CHAIN<TCHAIN>::~tagSERVER_CHAIN(void)
{
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Init(Int nHashSize)
{
    uIndex = 0;
    Chain[0].SetHash(nHashSize);
    Chain[1].SetHash(nHashSize);
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Exit(void)
{
    Chain[0].RemoveAll();
    Chain[1].RemoveAll();
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetClone(void)
{
    UInt uNext = (CAtomics::CompareExchange<UInt>(&uIndex, 0, 0) != 0) ? 0 : 1;
    Chain[uNext] = Chain[uIndex];
    return Chain[uNext];
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetCurr(void)
{
    return Chain[CAtomics::CompareExchange<UInt>(&uIndex, 0, 0)];
}

template <typename TCHAIN>
INLINE TCHAIN& tagSERVER_CHAIN<TCHAIN>::GetNext(void)
{
    return Chain[(CAtomics::CompareExchange<UInt>(&uIndex, 0, 0) != 0) ? 0 : 1];
}

template <typename TCHAIN>
INLINE void tagSERVER_CHAIN<TCHAIN>::Swap(void)
{
    if (CAtomics::CompareExchange<UInt>(&uIndex, 1, 0) != 0)
    {
        CAtomics::Decrement<UInt>(&uIndex);
    }
}

#endif // __SERVER_INFO_INL__
